/**** \file rsrcmanager/rsrcmanager.h
 * \brief Otter Resource Manager
 * \author JD <brossill@enseirb.fr>
 * \date 27.28.04.2007
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <signal.h>

#include "../log/log.h"
#include "prompt.h"

#include "../global.h"
#include "../utils.h"

#define xstr(s) str(s)
#define str(s) #s

msd_s msocket;

int yopd_pid;

void network_send(msd_s* msocket, npacket_s* np)
{
  if( OSOCK_send(msocket,np) != 0 )
  {
    if( OSOCK_send(msocket,np) != 0 )
    {
      fprintf(stderr,"Network error: Aborting.\n");
      return;
    }
  }

  if( kill(yopd_pid,SIGUSR1) == -1 )
  {
    fprintf(stderr,"Can't send signal to yopd[%d]. Aborting.\n",
            yopd_pid);
    exit(-1);
  }

  return;
}

pid_t get_yopd_pid(char* filename)
{
  FILE* fin = fopen(filename,"r");

  int pid;

  if(!fin)
  {
    fprintf(stderr,"Can't read %s to get yopd pid.\n",
            filename);
    return 0;
  }

  fscanf(fin,"%d", &pid);

  fclose(fin);

  return (pid_t)pid;
}

int main(int argc, char** argv)
{

  char buf[MAX_COMMAND_LINE_LENGTH];
  char* command = NULL;
  char* opt = NULL;
  char* opt2 = NULL;

  atexit(destroy);

  LOG_set_file("./prompt.log");

  if( OSOCK_connect(&msocket, TCP,
                    PROMPT_SERVER_ADDR,
                    PROMPT_SERVER_PORT,
                    SERVER)
      != 0 )
  {
    fprintf(stderr,"Can't set up TCP server at %s:%d.\n",
            PROMPT_SERVER_ADDR,
            PROMPT_SERVER_PORT);
    exit(-1);
  }

  yopd_pid = get_yopd_pid(PROMPT_SERVER_PID_FILE);

  if(!yopd_pid)
  {
    fprintf(stderr,"Can't send signal to yopd[%d]. Aborting.\n",
            yopd_pid);
    exit(-1);
  }

  // Send a signal to the server
  if( kill(yopd_pid,SIGUSR1) == -1 )
  {
    fprintf(stderr,"Can't send signal to yopd[%d]. Aborting.\n",
            yopd_pid);
    exit(-1);
  }

  printf("Signal sent to yopd[%d], waiting for server...\n",yopd_pid); 

  // Waiting for in connection
  if( OSOCK_validate_tcp_server(&msocket) )
  {
    fprintf(stderr,"Server refuse connection at %s:%d.\n",
            PROMPT_SERVER_ADDR,
            PROMPT_SERVER_PORT);

    OSOCK_disconnect(&msocket);

    exit(-1);
  }

  printf("Connexion established, acces granted.\n");

  while(1)
  {
    memset(buf,0,MAX_COMMAND_LINE_LENGTH);

    printf(">");
    scanf("%"xstr(MAX_COMMAND_LINE_LENGTH)"[^\n]",buf);

    //printf("#DBG#buf#%s\n",buf);

    // Void stdin
    char c;
    while ( ((c = getchar()) != '\n') && c != EOF);

    if( !(command = strtok(buf," ")) )
    {
      fprintf(stderr,PROMPT_WHAT"\n");
      continue;
    }

    //printf("#DBG#com#%s\n",command);

    if(!strcmp(command, PROMPT_COMMAND_SET     ) )
    {
      if( !(opt = strtok(NULL, " ")) )
      { // No options

        show_all_program_options();

      } // And nothing ...
      else
      { // ... matter

        change_option(opt);
      }

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_HELP    ) )
    {
      show_help();

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_LIST    ) )
    { 
      show_list();

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_GET     ) )
    {
      if( !(opt = strtok(NULL, " ")) )
      {
        fprintf(stderr,PROMPT_GET_NEED_OPT"\n");
        continue;
      }

      command_get(opt);

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_INFO    ) )
    {

      show_infos();

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_DOWNLOAD) )
    {

      show_downloads();

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_UPLOAD  ) )
    {

      show_uploads();

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_CONNECT ) )
    {

      if( !(opt = strtok(NULL, " ")) )
      {
        fprintf(stderr,PROMPT_CONNECT_NEED_OPT"\n");
        continue;
      }

      command_connect(opt);

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_RAW     ) )
    {

      if( !(opt = strtok(NULL, " ")) )
      {
        fprintf(stderr,PROMPT_RAW_NEED_OPT"\n");
        continue;
      }

      if( !(opt2 = strtok(NULL, " ")) )
      {
        fprintf(stderr,PROMPT_RAW_NEED_OPT"\n");
        continue;
      }

      char* pc;

      while( !(pc = strtok(NULL, " ")) )
        *(pc-1) =  ' ';

      command_raw(opt,opt2);

      continue;
    }

    if(!strcmp(command, PROMPT_COMMAND_EXIT ) )
    {
      break;
    }

    if(!strcmp(command, PROMPT_COMMAND_SINK ) )
    {
      sink_server();

      continue;
    }

    fprintf(stderr,PROMPT_WHAT"\n");

  }


  return 0;
}


void destroy(void)
{
  LOG_close();

  OSOCK_disconnect(&msocket);

  return;
}

void show_all_program_options(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_SET;

  npacket_s packet = { data,sizeof(char)};

  // Send packet
  network_send(&msocket, &packet);

  free(data);

  // Allocate to max (see OSOCK_receive...)
  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  printf("Avaible options are:\n");

  while( 1 )
  {
    if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data); 
      return;
    }

    // Receive
    if( OSOCK_receive(&packet,&msocket) < 0 )
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data);
      return;
    }


    ((char*)(packet.datap))[ packet.datalen ] = '\0';

    if( ((char*)packet.datap)[0] == PROMPT_END_PACKET ) 
      break;

    printf("- %s\n",(char*)(packet.datap));
  }

  free(data);

  return;
}


void change_option(char* opt)
{
  char* data = malloc( sizeof(char) + sizeof(char)*strlen(opt));

  memset(data,0,sizeof(char) + sizeof(char)*strlen(opt));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_SET;

  memcpy(data + ptr, opt, strlen(opt));

  npacket_s packet = { data,sizeof(char)+strlen(opt)};

  network_send(&msocket, &packet);

  free(data);

  // Receive the ACK
  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));


  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data); 
    return;
  }

  if( OSOCK_receive(&packet,&msocket) < 0 )
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data);
    return;
  }

  if( data[0] != PRV_OK )
  {
    fprintf(stderr,"Server can't do that:%d\n",data[0]);
  }
  else
  {
    printf("Done.\n");
  }

  free(data);

  return;
}


void show_help(void)
{

  printf("Available commands:\n");

  printf("%s [option=value]\n",PROMPT_COMMAND_SET     );
  printf("%s \n",PROMPT_COMMAND_HELP    );
  printf("%s\n",PROMPT_COMMAND_LIST    );
  printf("%s key\n",PROMPT_COMMAND_GET     );
  printf("%s\n",PROMPT_COMMAND_INFO    );
  printf("%s\n",PROMPT_COMMAND_DOWNLOAD);
  printf("%s\n",PROMPT_COMMAND_UPLOAD  );
  printf("%s ip:port\n",PROMPT_COMMAND_CONNECT );
  printf("%s ip:port command\n",PROMPT_COMMAND_RAW     );

  return;
}

void show_list(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_LIST;

  npacket_s packet = { data,sizeof(char)};

  network_send(&msocket, &packet);

  free(data);


  // Receiving ...

  //       12345678901234567890123456789012 12345678901       
  printf("|         KEY                    |   SIZE    |    NAME\n");

  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  int n=0,rv;

  while(1)
  {
    if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data); 
      return;
    }

    rv = OSOCK_receive(&packet,&msocket);

    // Socket close ?
    if(!rv)
      break;

    if( rv <0 )
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data);
      return;
    }

    ((char*)(packet.datap))[packet.datalen] = '\0';

    if( ((char*)(packet.datap))[0] == PROMPT_END_PACKET )
      break;

    switch(n)
    {
      case 0:
        printf("|%32.s",(char*)(packet.datap));
        break;

      case 1:
        printf("|%11.s",(char*)(packet.datap));
        break;

      case 2:
        printf("|%s\n",(char*)(packet.datap));
    }

    n= (n>2)?0:n+1;
  }

  free(data);

  return;
}

void command_get(char* opt)
{
  char* data = malloc( sizeof(char) + sizeof(filekey_t) );

  memset(data,0,sizeof(char) + sizeof(filekey_t));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_GET;

  memcpy(data + ptr,opt,sizeof(filekey_t));

  npacket_s packet = { data,sizeof(char) + sizeof(filekey_t)};

  network_send(&msocket, &packet);

  free(data);

  return;
}


void show_infos(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_INFO;

  npacket_s packet = { data,sizeof(char)};

  network_send(&msocket, &packet);

  free(data);


  // Receiving ...

  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data); 
    return;
  }

  int rv;

  rv = OSOCK_receive(&packet,&msocket);

  if( rv <= 0 )
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data);
    return;
  }

  ((char*)(packet.datap))[packet.datalen] = '\0';

  //printf("%s\n",(char*)(packet.datap));

  free(data);

  return;
}


void show_downloads(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_DOWNLOAD;

  npacket_s packet = { data,sizeof(char)};

  network_send(&msocket, &packet);

  free(data);


  // Receiving ...

  //       12345678901234567890123456789012 12345 123-      
  printf("|         KEY                    |PEERS| %% | NAME\n");

  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  int n=0,rv;

  while(1)
  {
    if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data); 
      return;
    }

    rv = OSOCK_receive(&packet,&msocket);

    // Socket close ?
    if(!rv)
      break;

    if( rv <0 )
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data);
      return;
    }

    ((char*)(packet.datap))[packet.datalen] = '\0';

    if( ((char*)(packet.datap))[0] == PROMPT_END_PACKET )
      break;

    switch(n)
    {
      case 0:
        printf("|%32.s",(char*)(packet.datap));
        break;

      case 1:
        printf("|%5.s",(char*)(packet.datap));
        break;

      case 2:
        printf("|%2.s%%",(char*)(packet.datap));
        break;

      case 3:
        printf("|%s\n",(char*)(packet.datap));
        break;

    }

    n= (n>3)?0:n+1;
  }

  free(data);

  return;
}


void show_uploads(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_UPLOAD;

  npacket_s packet = { data,sizeof(char)};

  network_send(&msocket, &packet);

  free(data);


  // Receiving ...

  //       12345678901234567890123456789012 12345       
  printf("|         KEY                    |PEERS| NAME\n");

  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  int n=0,rv;

  while(1)
  {  
    if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data); 
      return;
    }

    rv = OSOCK_receive(&packet,&msocket);

    // Socket close ?
    if(!rv)
      break;

    if( rv <0 )
    {
      fprintf(stderr,"Network error: Aborting.\n");
      free(data);
      return;
    }

    ((char*)(packet.datap))[packet.datalen] = '\0';

    if( ((char*)(packet.datap))[0] == PROMPT_END_PACKET )
      break;

    switch(n)
    {
      case 0:
        printf("|%32.s",(char*)(packet.datap));
        break;

      case 1:
        printf("|%5.s",(char*)(packet.datap));
        break;

      case 2:
        printf("|%s\n",(char*)(packet.datap));
        break;

    }

    n= (n>2)?0:n+1;
  }

  free(data);


  return;
}


void command_connect(char* opt)
{
  char* ip = NULL;
  char* port = NULL;

  if(!(ip = strtok(opt,":")))
  {
    fprintf(stderr,PROMPT_CONNECT_NEED_OPT"\n");
    return;
  }

  if(!(port = strtok(NULL,":")))
  {
    fprintf(stderr,PROMPT_CONNECT_NEED_OPT"\n");
    return;
  }   

  //printf("ip=%s port=%s\n",ip,port);

  int a,b,c,d,p;
  in_port_t iport;

  sscanf(ip,"%d.%d.%d.%d",&a,&b,&c,&d);

  sscanf(port,"%d",&p);

  iport = p;

  //printf("%x.%x.%x.%x %x\n",a,b,c,d,iport);

  char* data = malloc( sizeof(in_port_t) 
                       + 4*sizeof(char) + sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_CONNECT;

  data[ptr++] = a;
  data[ptr++] = b;
  data[ptr++] = c;
  data[ptr++] = d;

  memcpy(data + ptr,&iport,sizeof(in_port_t));

  npacket_s packet = { data, sizeof(in_port_t) 
    + 4*sizeof(char) + sizeof(char)};

  network_send(&msocket, &packet);

  free(data);

  // Receive the ACK
  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));


  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data); 
    return;
  }

  if( OSOCK_receive(&packet,&msocket) != 0 )
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data);
    return;
  }

  if( data[0] != PRV_OK )
  {
    fprintf(stderr,"Server can't do that:%d\n",data[0]);
  }
  else
  {
    printf("Done.\n");
  }

  return;
}


void command_raw(char* opt,char* command)
{
  char* ip = NULL;
  char* port = NULL;

  int cl = strlen(command);

  if( cl <= 0 )
    return;



  if(!(ip = strtok(opt,":")))
  {
    fprintf(stderr,PROMPT_CONNECT_NEED_OPT"\n");
    return;
  }

  if(!(port = strtok(NULL,":")))
  {
    fprintf(stderr,PROMPT_CONNECT_NEED_OPT"\n");
    return;
  }   

  //printf("ip=%s port=%s command=%s\n",ip,port,command);

  int a,b,c,d,p;
  in_port_t iport;

  sscanf(ip,"%d.%d.%d.%d",&a,&b,&c,&d);
  sscanf(port,"%d",&p);

  iport = p;

  //printf("%x.%x.%x.%x %x cl=%d\n",a,b,c,d,iport,cl);

  int L = sizeof(char) + cl*sizeof(char) 
    + sizeof(in_port_t) + 4*sizeof(char);


  if( L > MAX_NPACK_SIZE )
  {
    fprintf(stderr,
            "Warning oversized command detected: Aborting.\n");
    return;
  }

  char* data = malloc(L);

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_RAW;

  data[ptr++] = a;
  data[ptr++] = b;
  data[ptr++] = c;
  data[ptr++] = d;

  //printf("####%d %d\n",ptr,sizeof(in_port_t));

  memcpy(data + ptr,&iport,sizeof(in_port_t));
  ptr+=sizeof(in_port_t);

  memcpy(data + ptr,command, sizeof(char)*cl);

  npacket_s packet = { data, L};

  network_send(&msocket, &packet);

  free(data);

  // Receive the ACK
  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));


  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data); 
    return;
  }

  if( OSOCK_receive(&packet,&msocket) != 0 )
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data);
    return;
  }

  if( data[0] != PRV_OK )
  {
    fprintf(stderr,"Server can't do that:%d\n",data[0]);
  }
  else
  {
    printf("Done.\n");
  }


  return;
}

void sink_server(void)
{
  char* data = malloc( sizeof(char) );

  memset(data,0,sizeof(char) );

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  int ptr = 0;

  data[ptr++] = PC_SINK;

  npacket_s packet = { data,sizeof(char)};

  network_send(&msocket, &packet);

  free(data);

  // Receive the ACK
  data = malloc( (MAX_NPACK_SIZE+1)*sizeof(char));

  if( !data)
  {
    fprintf(stderr,"A malloc just failed ! Run ! Ruuuun !\n");
    exit(-1);
  }

  packet.datap = data;

  if( OSOCK_watch_connection(&msocket,PROMPT_TIMEOUT))
  {
    fprintf(stderr,"Network error: Aborting.\n");
    free(data); 
    return;
  }

  int rv;
  if( (rv = OSOCK_receive(&packet,&msocket)) < 0 )
  {
    fprintf(stderr,"Network error [%d]: Aborting.\n",
            rv);
    free(data);
    return;
  }

  if( data[0] != PRV_OK )
  {
    fprintf(stderr,"Server can't do that:%d\n",data[0]);
  }
  else
  {
    printf("Done.\n");
  }

  free(data);

  return;
}


